﻿/******************************************************
* author :  cwj
* email  :  chenwenji_360@live.com 
* history:  created by cwj 2015/7/24 9:10:15 
* clrversion :4.0.30319.18444
******************************************************/

using Soul.DataAccess.Common.ORM;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace Soul.DataAccess
{
    /// <summary>
    /// 自动匹配属性赋值拓展类
    /// </summary>
    public static class AutoMapperExtession
    {
        /// <summary>
        /// 创建匹配对象接口
        /// </summary>
        /// <typeparam name="TValue">目标类型</typeparam>
        /// <param name="value">目标值</param>
        /// <returns>匹配接口</returns>
        public static IMapperEntity<TValue> BeginMapper<TValue>(this TValue value)
        {
            return new MapperEntity<TValue>(value);
        }
    }

    /// <summary>
    /// 匹配对象接口
    /// </summary>
    /// <typeparam name="TValue">目标类型</typeparam>
    public interface IMapperEntity<TValue>// : IMapperEntity
    {
        /// <summary>
        /// 填充对象
        /// </summary>
        /// <param name="target">目标值</param>
        /// <returns>匹配对象接口</returns>
        IMapperEntity<TValue> Fill(object target);
        /// <summary>
        /// 绑定目标值的属性
        /// </summary>
        /// <param name="bindingProperty">目标值的属性</param>
        /// <returns>匹配对象接口</returns>
        IMapperEntity<TValue> Bind(Expression<Func<TValue, object>> bindingProperty);
        /// <summary>
        /// 绑定源值的属性
        /// </summary>
        /// <typeparam name="TTarget">源值类型</typeparam>
        /// <param name="targetPropety">源值属性</param>
        /// <param name="format">格式转化</param>
        /// <returns>匹配对象接口</returns>
        IMapperEntity<TValue> To<TTarget>(Expression<Func<TTarget, object>> targetPropety, Func<object, object> format = null);
        /// <summary>
        /// 开始匹配，并得到目标值
        /// </summary>
        /// <returns>目标值</returns>
        TValue Build();
    }

    internal class MapperEntity<TValue> : IMapperEntity<TValue>
    {
        private Dictionary<Type,object> Targets { get; set; }
        //public Dictionary<Type,PropertyInvokerDic> TargetPropertyDic { get; private set; }

        private TValue Value { get; set; }
        private PropertyInvokerDic ValueProperties { get; set; }

        private Dictionary<IPropertyInvoker, Func<object, object>> Formater { get; set; }

        private Dictionary<IPropertyInvoker, IPropertyInvoker> MapperProperty { get; set; }

        private Queue<Expression<Func<TValue,object>>> BindProperties { get; set; }

        public MapperEntity(TValue value)
        {
            this.Value = value;
            this.ValueProperties = DynamicAssignment.Instance.GetDictionary(typeof(TValue));
            Targets = new Dictionary<Type, object>();
            //TargetPropertyDic = new Dictionary<Type, PropertyInvokerDic>();
            Formater = new Dictionary<IPropertyInvoker, Func<object, object>>();
            MapperProperty = new Dictionary<IPropertyInvoker, IPropertyInvoker>();
            this.BindProperties = new Queue<Expression<Func<TValue, object>>>();
        }

        private void Mapper(string valuePropertyName, IPropertyInvoker mapper, Func<object, object> formate = null, bool isReSet = false)
        {
            var valueProperty = ValueProperties[valuePropertyName];
            if (isReSet || !MapperProperty.ContainsKey(valueProperty))
            {
                MapperProperty[valueProperty] = mapper;
                if (Formater.ContainsKey(mapper)) Formater.Remove(mapper);
                if (formate != null)
                {
                    Formater[mapper] = formate;
                }
            }
        }

        public IMapperEntity<TValue> Fill(object target)
        {
            var targetType = target.GetType();
            this.Targets.Add(targetType, target);
            var properties = DynamicAssignment.Instance.GetDictionary(targetType);
            foreach (var item in properties)
            {
                if (!ValueProperties.ContainsKey(item.Key))
                {
                    this.Mapper(item.Key, item.Value);
                }
            }
            return this;
        }

        public IMapperEntity<TValue> Bind(Expression<Func<TValue, object>> bindingProperty)
        {
            this.BindProperties.Enqueue(bindingProperty);
            return this;
        }

        public IMapperEntity<TValue> To<TTarget>(Expression<Func<TTarget, object>> targetPropety,Func<object,object> format = null)
        {
            var valueProperty = this.BindProperties.Dequeue();
            if (valueProperty == null) throw new ArgumentException("找不到绑定的属性,请先调用Bind");
            this.MapperPorpety<TTarget>(valueProperty, targetPropety, format);
            return this;
        }

        public TValue Build()
        {
            foreach (var item in MapperProperty)
            {
                var obj = Targets[item.Value.PropertyInfo.DeclaringType];
                var getValue = item.Value.GetValue(obj);
                if (Formater.ContainsKey(item.Value))
                {
                    var format = Formater[item.Value];
                    getValue = format(getValue);
                }
                item.Key.SetValue(this.Value, getValue);
            }

            return this.Value;
        }

        private void MapperPorpety<TTarget>(Expression<Func<TValue, object>> valueProperty, Expression<Func<TTarget, object>> targetProperty, Func<object, object> format = null)
        {
            //var map = mapper as MapperEntity<TValue>;
            var property = DynamicAssignment.Instance.GetDictionary(typeof(TTarget));

            var tagetPropertyName = GetPropetyName(targetProperty);
            var valuePropertyName = GetPropetyName(valueProperty);

            this.Mapper(valuePropertyName, property[tagetPropertyName], format, true);
        }

        private static string GetPropetyName(LambdaExpression exp)
        {
            Expression expTemp = exp.Body;
            if (expTemp.NodeType == ExpressionType.Convert)
            {
                var temp = expTemp as UnaryExpression;
                expTemp = temp.Operand;
            }
            if (expTemp.NodeType != ExpressionType.MemberAccess) throw new NotSupportedException("不支持的Lamb表达式");
            return (expTemp as MemberExpression).Member.Name.ToLower();
        }
        //private object GetValue()
    }

}
